package model;

/**
 * Course.java
 * Purpose: this class represents a course that a student might take
 * 
 * @author Hans Brems
 * @version 1.0 03/11/2011
 */

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import persistency.writer.Writable;

import model.enums.CourseMinutes;
import model.enums.Credits;

public class Course implements Writable, Cloneable, Comparable<Course> {
	
	private int code;
	private Credits credits;
	private CourseMinutes courseMinutes;
	private String name;
	private Collection<Course> requiredCourses  = new ArrayList<Course>();;
	private List<OfferedCourse> offeredcourses  = new ArrayList<OfferedCourse>();

	/** Constructs a course object */
	public Course() {
	}
	
	/** Constructs a course object */
	public Course(int code, String name) {
		this();
		this.code = code;
		this.name = name;
	}
	
	/** Constructs a course object */
	public Course(int code, String name, int credits, int courseMinutes) {
		this(code,name);
		this.setCredits(credits);
		this.setCourseMinutes(courseMinutes);
	}

	/** Returns the course code **/
	public int getCode() {
		return code;
	}
	
	/** Sets the course code **/
	public void setCode(int code) {
		this.code = code;
	}
	
	/** Returns the course name **/
	public String getName() {
		return name;
	}
	
	/** Sets the course name **/
	public void setName(String name) {
		this.name = name;
	}

	/** Returns the amount of credits one earns from taking this course **/
	public int getCredits() {
		return this.credits.getValue();
	}
	
	/** Sets the amount of credits one earns from taking this course **/
	public void setCredits (int credits) {
		switch (credits) {
			case 3:
				this.credits = Credits.MIN;
				break;
			case 6:
				this.credits = Credits.MAX;
				break;
			default:
				this.credits = Credits.ILLEGAL;
				break;
		}
	}

	/** Returns the total amount of minutes per week that this course is being taught **/
	public int getCourseMinutes() {
		return this.courseMinutes.getValue();
	}
	
	/** Sets the total amount of minutes per week that this course is being taught **/
	public void setCourseMinutes (int courseTime) {
		switch (courseTime) {
			case 120:
				this.courseMinutes = CourseMinutes.MIN;
				break;
			case 240:
				this.courseMinutes = CourseMinutes.MAX;
				break;
			default:
				this.courseMinutes = CourseMinutes.ILLEGAL;
				break;
		}
	}
	
	/** Returns the list of required completed course before one can start this course **/
	public Collection<Course> getRequiredCourses() {
		return this.requiredCourses;
	}
	
	/** Sets the list of required completed courses before one can start this course **/
	public void setRequiredCourses(Collection<Course> requiredCourses) {
		this.requiredCourses = requiredCourses;
	}

	/** Returns a list of scheduled instances for this course */
	public List<OfferedCourse> getOfferedcourses() {
		return offeredcourses;
	}

	/** Sets the list of scheduled instances for this course */
	public void setOfferedcourses(List<OfferedCourse> offeredcourses) {
		this.offeredcourses = offeredcourses;
	}

	/** Adds a course which is required to follow the current course **/
	public void addRequiredCourse(Course course) throws Exception {

		// Make sure the course is not adding to itself
		if (this == course) {
			throw new Exception("A course cannot be added to itself.");
		}
		
		// Make sure the course is not already listed
		for (Course requiredCourse : this.requiredCourses) {
			if (requiredCourse == course) {
				throw new Exception("This course is already listed");
			}
		}
		
		// Add course to course list
		this.requiredCourses.add(course);
	}
	
	/** Returns a list of required courses which are required to follow the current course **/
	public String printRequiredCourses() {
		String s = new String();
		// Add course name to list
		for (Course requiredCourse : this.requiredCourses) {
			s += requiredCourse.toString() + "\n";
		}
		return s;
	}

	/** Checks for equality with another object */
	@Override
	public boolean equals(Object object) {
		
		Course otherCourse;
		
		// Check for same reference
		if (this == object) {
			return true;
		}
		
		// Check if object type is equal
		if (object == null || object.getClass() != this.getClass()) {
			return false;
		}
		
		// Cast object to correct class
		otherCourse = (Course)object;
			
		// Check if object properties are equal
		return otherCourse.getCode() == this.code;
	}
	
	/** Returns an integer representation of the object **/
	@Override
	public int hashCode() {
        return this.code;
	}
	
	/** Returns a string representation of the object **/
	@Override
	public String toString() {
		//return String.format("%s (credits: %d) (minutes: %d)", this.name, this.credits.getValue(), this.courseMinutes.getValue());
		return name;
	}
	
	/** Clones the object **/
	@Override
	public Object clone() throws CloneNotSupportedException {
		
		// Copy parent
		Course clone = (Course)super.clone();
		
		// Set properties
		clone.code = this.code;
		clone.name = this.name;
		clone.credits = this.credits;
		clone.courseMinutes = this.courseMinutes;
		clone.requiredCourses = this.requiredCourses;
		clone.offeredcourses = this.offeredcourses;
		
		// Return result
		return clone;
	}

	/** Compares the object to another object **/
	@Override
	public int compareTo(Course course) {
		return 0;
	}

	@Override
	public String toDataString() {
		return code + "\t" + name + "\t" + getCourseMinutes() + "\t" + getCredits() + "\n";
	}
	
	@Override
	public Object getPrimaryKey(){
		return code;
	}
}